home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume36 / log_tcp / part02 < prev    next >
Encoding:
Text File  |  1993-03-07  |  52.1 KB  |  1,678 lines

  1. Newsgroups: comp.sources.misc
  2. From: wietse@wzv.win.tue.nl (Wietse Venema)
  3. Subject: v36i005:  log_tcp - TCP/IP daemon wrapper, v5.0, Part02/03
  4. Message-ID: <1993Mar8.041353.22488@sparky.imd.sterling.com>
  5. X-Md4-Signature: adb836bf33cda8e67f0a4158c95ea994
  6. Date: Mon, 8 Mar 1993 04:13:53 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: wietse@wzv.win.tue.nl (Wietse Venema)
  10. Posting-number: Volume 36, Issue 5
  11. Archive-name: log_tcp/part02
  12. Environment: UNIX
  13. Supersedes: log_tcp: Volume 30, Issue 79-80
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then unpack
  17. # it by saving it into a file and typing "sh file".  To overwrite existing
  18. # files, type "sh file -c".  You can also feed this as standard input via
  19. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  20. # will see the following message at the end:
  21. #        "End of archive 2 (of 3)."
  22. # Contents:  fromhost.c hosts_access.5 percent_x.c rfc931.c shell_cmd.c
  23. #   strcasecmp.c strtok.c tcpd.8 tcpd.c try.c
  24. # Wrapped by wietse@wzv on Sun Mar  7 22:58:26 1993
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f fromhost.c -a "${1}" != "-c" ; then 
  27.   echo shar: Will not over-write existing file \"fromhost.c\"
  28. else
  29. echo shar: Extracting \"fromhost.c\" \(8144 characters\)
  30. sed "s/^X//" >fromhost.c <<'END_OF_fromhost.c'
  31. X /*
  32. X  * fromhost() determines the type of connection (datagram, stream), the name
  33. X  * and address of the host at the other end of standard input, and the
  34. X  * remote user name (if RFC 931 lookups are enabled). A host name of "stdin"
  35. X  * is returned if the program is run from a tty. The value "unknown" is
  36. X  * returned as a placeholder for information that could not be looked up.
  37. X  * All results are in static memory.
  38. X  * 
  39. X  * The return status is (-1) if the remote host pretends to have someone elses
  40. X  * host name, otherwise a zero status is returned.
  41. X  * 
  42. X  * Diagnostics are reported through syslog(3).
  43. X  * 
  44. X  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  45. X  */
  46. X
  47. X#ifndef lint
  48. Xstatic char sccsid[] = "@(#) fromhost.c 1.7 93/03/07 22:47:34";
  49. X#endif
  50. X
  51. X/* System libraries. */
  52. X
  53. X#include <sys/types.h>
  54. X#include <sys/param.h>
  55. X#include <sys/socket.h>
  56. X#include <netinet/in.h>
  57. X#include <netdb.h>
  58. X#include <stdio.h>
  59. X#include <syslog.h>
  60. X#include <errno.h>
  61. X
  62. Xextern char *inet_ntoa();
  63. Xextern char *strncpy();
  64. Xextern char *strcpy();
  65. X
  66. X/* In case not defined in <sys/param.h>. */
  67. X
  68. X#ifndef MAXHOSTNAMELEN
  69. X#define MAXHOSTNAMELEN    1024        /* string with host name */
  70. X#endif
  71. X
  72. X/* Local stuff. */
  73. X
  74. X#include "log_tcp.h"
  75. X
  76. X/* Forward declarations. */
  77. X
  78. Xstatic int matchname();
  79. X
  80. X/* The following are to be used in assignment context, not in comparisons. */
  81. X
  82. X#define    GOOD    1
  83. X#define    BAD    0
  84. X
  85. X/* Initially, we know nothing about the origin of the connection. */
  86. X
  87. Xstatic struct from_host from_unknown = {
  88. X    0,                    /* connected/unconnected */
  89. X    FROM_UNKNOWN,            /* remote host name */
  90. X    FROM_UNKNOWN,            /* remote host address */
  91. X    "",                    /* remote user name */
  92. X};
  93. X
  94. X /*
  95. X  * With early SunOS 5 versions, recvfrom() does not completely fill in the
  96. X  * source address structure when doing a non-destructive read. The following
  97. X  * code works around the problem. It does no harm on "normal" systems.
  98. X  */
  99. X
  100. X#ifdef RECVFROM_BUG
  101. X
  102. Xstatic int fix_recvfrom(sock, buf, buflen, flags, from, fromlen)
  103. Xint     sock;
  104. Xchar   *buf;
  105. Xint     buflen;
  106. Xint     flags;
  107. Xstruct sockaddr *from;
  108. Xint     *fromlen;
  109. X{
  110. X    int     ret;
  111. X
  112. X    /* Assume that both ends of a socket belong to the same address family. */
  113. X
  114. X    if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) {
  115. X    if (from->sa_family == 0) {
  116. X        struct sockaddr my_addr;
  117. X        int     my_addr_len = sizeof(my_addr);
  118. X
  119. X        if (getsockname(0, &my_addr, &my_addr_len)) {
  120. X        syslog(LOG_ERR, "getsockname: %m");
  121. X        } else {
  122. X        from->sa_family = my_addr.sa_family;
  123. X        }
  124. X    }
  125. X    }
  126. X    return (ret);
  127. X}
  128. X
  129. X#define recvfrom fix_recvfrom
  130. X#endif
  131. X
  132. X /*
  133. X  * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an
  134. X  * error in case of a datagram-oriented socket. Instead, they claim that all
  135. X  * UDP requests come from address 0.0.0.0. The following code works around
  136. X  * the problem. It does no harm on "normal" systems.
  137. X  */
  138. X
  139. X#ifdef GETPEERNAME_BUG
  140. X
  141. Xstatic int fix_getpeername(sock, sa, len)
  142. Xint     sock;
  143. Xstruct sockaddr *sa;
  144. Xint    *len;
  145. X{
  146. X    int     ret;
  147. X    struct sockaddr_in *sin = (struct sockaddr_in *) sa;
  148. X
  149. X    if ((ret = getpeername(sock, sa, len)) >= 0
  150. X    && sa->sa_family == AF_INET
  151. X    && sin->sin_addr.s_addr == 0) {
  152. X    errno = ENOTCONN;
  153. X    return (-1);
  154. X    } else {
  155. X    return (ret);
  156. X    }
  157. X}
  158. X
  159. X#define    getpeername    fix_getpeername
  160. X#endif
  161. X
  162. X/* fromhost - find out what is at the other end of standard input */
  163. X
  164. Xint     fromhost(f)
  165. Xstruct from_host *f;
  166. X{
  167. X    static struct sockaddr sa;
  168. X    struct sockaddr_in *sin = (struct sockaddr_in *) (&sa);
  169. X    struct hostent *hp;
  170. X    int     length = sizeof(sa);
  171. X    char    buf[BUFSIZ];
  172. X    static char addr_buf[FROM_ADDRLEN];
  173. X    static char name_buf[MAXHOSTNAMELEN];
  174. X
  175. X    /*
  176. X     * There are so many results and so many early returns that it seems
  177. X     * safest to first initialize all results to UNKNOWN.
  178. X     */
  179. X
  180. X    *f = from_unknown;
  181. X
  182. X    /*
  183. X     * Look up the remote host address. Hal R. Brand <BRAND@addvax.llnl.gov>
  184. X     * suggested how to get the remote host info in case of UDP connections:
  185. X     * peek at the first message without actually looking at its contents.
  186. X     */
  187. X
  188. X    if (getpeername(0, &sa, &length) >= 0) {    /* assume TCP request */
  189. X    f->sock_type = FROM_CONNECTED;
  190. X    } else {
  191. X    switch (errno) {
  192. X    default:
  193. X        if (isatty(0))            /* stdin is not a socket */
  194. X        f->name = "stdin";
  195. X        else
  196. X        syslog(LOG_ERR, "getpeername: %m");    /* other, punt */
  197. X        return (0);
  198. X    case ENOTCONN:                /* assume UDP request */
  199. X        length = sizeof(sa);
  200. X        if (recvfrom(0, buf, sizeof(buf), MSG_PEEK, &sa, &length) < 0) {
  201. X        syslog(LOG_ERR, "recvfrom: %m");
  202. X        return (0);
  203. X        }
  204. X#ifdef really_paranoid
  205. X        memset(buf, 0 sizeof(buf));
  206. X#endif
  207. X        f->sock_type = FROM_UNCONNECTED;
  208. X        break;
  209. X    }
  210. X    }
  211. X
  212. X    /*
  213. X     * At present, we can only deal with the AF_INET address family. Some
  214. X     * implementations of System V religion never fill in the address family
  215. X     * field in case of UDP connections. If that happens, you may want to
  216. X     * take the chance and assume that we're dealing with TCP/IP anyway.
  217. X     */
  218. X
  219. X#ifdef ADDRESS_FAMILY_BUG
  220. X    if (sa.sa_family == 0)
  221. X    sa.sa_family = AF_INET;
  222. X#endif
  223. X    if (sa.sa_family != AF_INET) {
  224. X    syslog(LOG_ERR, "unexpected address family %ld", (long) sa.sa_family);
  225. X    return (0);
  226. X    }
  227. X    /* Save the host address. A later inet_ntoa() call may clobber it. */
  228. X
  229. X    f->sin = sin;
  230. X    f->addr = strcpy(addr_buf, inet_ntoa(sin->sin_addr));
  231. X
  232. X    /* Look up the remote user name. Does not work for UDP services. */
  233. X
  234. X#if defined(RFC931) && !defined(USER_AT_HOST) && !defined(RFC931_OPTION)
  235. X    if (f->sock_type == FROM_CONNECTED)
  236. X    f->user = rfc931_name(sin);
  237. X#endif
  238. X
  239. X    /* Look up the remote host name. */
  240. X
  241. X    if ((hp = gethostbyaddr((char *) &sin->sin_addr,
  242. X                sizeof(sin->sin_addr),
  243. X                AF_INET)) == 0) {
  244. X    return (0);
  245. X    }
  246. X    /* Save the host name. A later gethostbyxxx() call may clobber it. */
  247. X
  248. X    f->name = strncpy(name_buf, hp->h_name, sizeof(name_buf) - 1);
  249. X    name_buf[sizeof(name_buf) - 1] = 0;
  250. X
  251. X    /*
  252. X     * Verify that the host name does not belong to someone else. If host
  253. X     * name verification fails, pretend that the host name lookup failed.
  254. X     */
  255. X
  256. X    if (matchname(f->name, sin->sin_addr)) {
  257. X    return (0);
  258. X    } else {
  259. X    f->name = FROM_UNKNOWN;
  260. X    return (-1);                /* verification failed */
  261. X    }
  262. X}
  263. X
  264. X/* matchname - determine if host name matches IP address */
  265. X
  266. Xstatic int matchname(remotehost, addr)
  267. Xchar   *remotehost;
  268. Xstruct in_addr addr;
  269. X{
  270. X    struct hostent *hp;
  271. X    int     i;
  272. X
  273. X    if ((hp = gethostbyname(remotehost)) == 0) {
  274. X
  275. X    /*
  276. X     * Unable to verify that the host name matches the address. This may
  277. X     * be a transient problem or a botched name server setup. We decide
  278. X     * to play safe.
  279. X     */
  280. X
  281. X    syslog(LOG_ERR, "gethostbyname(%s): lookup failure", remotehost);
  282. X    return (BAD);
  283. X
  284. X    } else {
  285. X
  286. X    /*
  287. X     * Make sure that gethostbyname() returns the "correct" host name.
  288. X     * Unfortunately, gethostbyname("localhost") sometimes yields
  289. X     * "localhost.domain". Since the latter host name comes from the
  290. X     * local DNS, we just have to trust it (all bets are off if the local
  291. X     * DNS is perverted). We always check the address list, though.
  292. X     */
  293. X
  294. X    if (strcasecmp(remotehost, hp->h_name)
  295. X        && strcasecmp(remotehost, "localhost")) {
  296. X        syslog(LOG_ERR, "host name/name mismatch: %s != %s",
  297. X           remotehost, hp->h_name);
  298. X        return (BAD);
  299. X    }
  300. X    /* Look up the host address in the address list we just got. */
  301. X
  302. X    for (i = 0; hp->h_addr_list[i]; i++) {
  303. X        if (memcmp(hp->h_addr_list[i], (caddr_t) & addr, sizeof(addr)) == 0)
  304. X        return (GOOD);
  305. X    }
  306. X
  307. X    /*
  308. X     * The host name does not map to the original host address. Perhaps
  309. X     * someone has compromised a name server. More likely someone botched
  310. X     * it, but that could be dangerous, too.
  311. X     */
  312. X
  313. X    syslog(LOG_ERR, "host name/address mismatch: %s != %s",
  314. X           inet_ntoa(addr), hp->h_name);
  315. X    return (BAD);
  316. X    }
  317. X}
  318. X
  319. X#ifdef TEST
  320. X
  321. X/* Code for stand-alone testing. */
  322. X
  323. Xmain(argc, argv)
  324. Xint     argc;
  325. Xchar  **argv;
  326. X{
  327. X    struct from_host from;
  328. X
  329. X#ifdef LOG_MAIL
  330. X    (void) openlog(argv[0], LOG_PID, FACILITY);
  331. X#else
  332. X    (void) openlog(argv[0], LOG_PID);
  333. X#endif
  334. X    (void) fromhost(&from);
  335. X    printf("%s\n", hosts_info(&from));
  336. X    return (0);
  337. X}
  338. X
  339. X#endif
  340. END_OF_fromhost.c
  341. if test 8144 -ne `wc -c <fromhost.c`; then
  342.     echo shar: \"fromhost.c\" unpacked with wrong size!
  343. fi
  344. # end of overwriting check
  345. fi
  346. if test -f hosts_access.5 -a "${1}" != "-c" ; then 
  347.   echo shar: Will not over-write existing file \"hosts_access.5\"
  348. else
  349. echo shar: Extracting \"hosts_access.5\" \(9533 characters\)
  350. sed "s/^X//" >hosts_access.5 <<'END_OF_hosts_access.5'
  351. X.TH HOSTS_ACCESS 5
  352. X.SH NAME
  353. Xhosts_access \- format of host access control files
  354. X.SH DESCRIPTION
  355. XThis manual page describes a simple access control language that is
  356. Xbased on client (host name or address) and server (process name)
  357. Xpatterns.  Examples are given at the end. The impatient reader can skip
  358. Xto the EXAMPLES section for a quick introduction.
  359. X.PP
  360. XIn the following text, \fIdaemon\fR is the the process name of a
  361. Xnetwork daemon process, and \fIclient\fR is the name and/or address of
  362. Xa host requesting service. Network daemon process names are specified
  363. Xin the inetd configuration file.
  364. X.SH ACCESS CONTROL FILES
  365. XThe access control software consults two files:
  366. X.IP o
  367. XAccess will be granted when a (daemon,client) pair matches an entry in
  368. Xthe \fI/etc/hosts.allow\fR file.
  369. X.IP o
  370. XOtherwise, access will be denied when a (daemon,client) pair matches an
  371. Xentry in the \fI/etc/hosts.deny\fR file.
  372. X.IP o
  373. XOtherwise, access will be granted.
  374. X.PP
  375. XA non-existing access control file is treated as if it were an empty
  376. Xfile. Thus, access control can be turned off by providing no access
  377. Xcontrol files.
  378. X.SH ACCESS CONTROL RULES
  379. XEach access control file consists of zero or more lines of text.  These
  380. Xlines are processed in order of appearance. The search terminates when a
  381. Xmatch is found.
  382. X.IP o
  383. XA newline character is ignored when it is preceded by a backslash
  384. Xcharacter.
  385. X.IP o
  386. XBlank lines or lines that begin with a `#\' character are ignored.
  387. X.IP o
  388. XAll other lines should satisfy the following format, things between []
  389. Xbeing optional:
  390. X.sp
  391. X.ti +3
  392. Xdaemon_list : client_list [ : shell_command ]
  393. X.PP
  394. X\fIdaemon_list\fR is a list of one or more daemon process names
  395. X(argv[0] values) or wildcards (see below).  
  396. X.PP
  397. X\fIclient_list\fR is a list
  398. Xof one or more host names, host addresses, patterns or wildcards (see
  399. Xbelow) that will be matched against the remote host name or address.
  400. X.PP
  401. XList elements should be separated by blanks and/or commas.  
  402. X.PP
  403. XWith the exception of NIS (YP) netgroup lookups, all access control
  404. Xchecks are case insensitive.
  405. X.br
  406. X.ne 4
  407. X.SH PATTERNS
  408. XThe access control language implements the following patterns:
  409. X.IP o
  410. XA string that begins with a `.\' character.  A client name or address
  411. Xis matched if its last components match the specified pattern.  For
  412. Xexample, the pattern `.tue.nl\' matches the host name
  413. X`wzv.win.tue.nl\'.
  414. X.IP o
  415. XA string that ends with a `.\' character.  A client name or address is
  416. Xmatched if its first fields match the given string.  For example, the
  417. Xpattern `131.155.\' matches the address of (almost) every host on the
  418. XEind\%hoven University network (131.155.x.x).
  419. X.IP o
  420. XA string that begins with a `@\' character is treated as a netgroup
  421. Xname.  Netgroups are usually supported on systems with NIS (formerly
  422. XYP) data bases. A client host name is matched if it is a (host) member
  423. Xof the specified netgroup.
  424. X.IP o
  425. XAn expression of the form `n.n.n.n/m.m.m.m\' is interpreted as a
  426. X`net/mask\' pair. A client address is matched if `net\' is equal to the
  427. Xbitwise AND of the address and the `mask\'. For example, the net/mask
  428. Xpattern `131.155.72.0/255.255.254.0\' matches every address in the
  429. Xrange `131.155.72.0\' through `131.155.73.255\'.
  430. X.SH WILDCARDS
  431. XThe access control language supports explicit wildcards:
  432. X.IP ALL
  433. XIf this token appears in a daemon_list, it matches all network daemon
  434. Xprocess names.  If the ALL token appears in a client_list, it matches
  435. Xall client names and addresses.
  436. X.IP LOCAL
  437. XMatches any string that does not contain a dot character.
  438. XTypical use is in client_lists.
  439. X.IP UNKNOWN
  440. XMatches any host whose name and/or address lookup failed.  Should be
  441. Xused with care, because host names may also be unavailable due to
  442. Xtemporary name server problems.
  443. X.IP FAIL
  444. XLike the ALL wildcard, but causes the software to pretend that the scan
  445. Xof the current access control table fails. FAIL is being phased out; it
  446. Xwill become an undocumented feature. The EXCEPT operator (see below) is
  447. Xa much cleaner alternative.
  448. X.br
  449. X.ne 6
  450. X.SH OPERATORS
  451. X.IP EXCEPT
  452. XIntended use is of the form: `list_1 EXCEPT list_2\'; this construct
  453. Xmatches anything that matches \fIlist_1\fR unless it matches
  454. X\fIlist_2\fR.  This construct can be used in daemon_lists and in
  455. Xclient_lists. The EXCEPT operator can be nested and is
  456. Xright-associative (just like C's assignment operator).
  457. X.br
  458. X.ne 6
  459. X.SH SHELL COMMANDS
  460. XIf the first-matched access control rule contains a shell command, that
  461. Xcommand is subjected to the following substitutions:
  462. X.IP %a
  463. Xexpands to the remote host address.
  464. X.IP %c
  465. Xexpands to client information: user@host, user@address, a host name, or
  466. Xjust an address, depending on how much information is available.
  467. X.IP %h
  468. Xexpands to the remote host name (or address, if the host name is
  469. Xunavailable).
  470. X.IP %d
  471. Xexpands to the daemon process name (argv[0] value).
  472. X.IP %p
  473. Xexpands to the daemon process id.
  474. X.IP %u
  475. Xexpands to the remote user name (or "unknown").
  476. X.IP %%
  477. Xexpands to a single `%\' character.
  478. X.PP
  479. XCharacters in % expansions that may confuse the shell are replaced by
  480. Xunderscores.
  481. XThe result is executed by a \fI/bin/sh\fR child process with standard
  482. Xinput, output and error connected to \fI/dev/null\fR.  Specify an `&\'
  483. Xat the end of the command if you do not want to wait until it has
  484. Xcompleted.
  485. X.PP
  486. XShell commands should not rely on the PATH setting of the inetd.
  487. XInstead, they should use absolute path names, or they should begin with
  488. Xan explicit PATH=whatever statement.
  489. X.SH EXAMPLES
  490. XThe language is flexible enough that different types of access control
  491. Xpolicy can be expressed with a minimum of fuss. Although the language
  492. Xuses two access control tables, the most common policies can be
  493. Ximplemented with one of the tables being trivial or even empty.
  494. X.PP
  495. XWhen reading the examples below it is important to realize that the
  496. Xallow table is scanned before the deny table, that the search
  497. Xterminates when a match is found, and that access is granted when no
  498. Xmatch is found at all.
  499. X.PP
  500. XThe examples use host and domain names. They can be improved by
  501. Xincluding address and/or network/netmask information, to reduce the
  502. Ximpact of temporary name server lookup failures.
  503. X.SH MOSTLY CLOSED
  504. XIn this case, access is denied by default. Only explicitly authorized
  505. Xhosts are permitted access. 
  506. X.PP
  507. XThe default policy (no access) is implemented with a trivial deny
  508. Xfile:
  509. X.PP
  510. X.ne 2
  511. X/etc/hosts.deny: 
  512. X.in +3
  513. XALL: ALL
  514. X.PP
  515. XThis denies all service to all hosts, unless they are permitted access
  516. Xby entries in the allow file.
  517. X.PP
  518. XThe explicitly authorized hosts are listed in the allow file.
  519. XFor example:
  520. X.PP
  521. X.ne 2
  522. X/etc/hosts.allow: 
  523. X.in +3
  524. XALL: LOCAL @some_netgroup
  525. X.br
  526. XALL: .foobar.edu EXCEPT terminalserver.foobar.edu
  527. X.PP
  528. XThe first rule permits access to all services from hosts in the local
  529. Xdomain (no `.\' in the host name) and from members of the
  530. X\fIsome_netgroup\fP netgroup. The second rule permits access to all
  531. Xservices from all hosts in the \fI.foobar.edu\fP domain, with the
  532. Xexception of \fIterminalserver.foobar.edu\fP.
  533. X.SH MOSTLY OPEN
  534. XHere, access is granted by default; only explicitly specified hosts are
  535. Xrefused service. 
  536. X.PP
  537. XThe default policy (access granted) makes the allow file redundant so
  538. Xthat it can be omitted.  The explicitly non-authorized hosts are listed
  539. Xin the deny file. For example:
  540. X.PP
  541. X/etc/hosts.deny:
  542. X.in +3
  543. XALL: some.host.name, .some.domain
  544. X.br
  545. XALL EXCEPT in.fingerd: other.host.name, .other.domain
  546. X.PP
  547. XThe first rule denies some hosts all services; the second rule still
  548. Xpermits finger requests from other hosts.
  549. X.SH BOOBY TRAPS
  550. XThe next example permits tftp requests from hosts in the local domain.
  551. XRequests from any other hosts are denied. Instead of the requested
  552. Xfile, a finger probe is sent to the offending host. The result is
  553. Xmailed to the superuser.
  554. X.PP
  555. X.ne 2
  556. X/etc/hosts.allow:
  557. X.in +3
  558. X.nf
  559. Xin.tftpd: LOCAL, .my.domain
  560. X.PP
  561. X.ne 2
  562. X/etc/hosts.deny:
  563. X.in +3
  564. X.nf
  565. Xin.tftpd: ALL: (/usr/ucb/finger -l @%h | /usr/ucb/mail -s %d-%h root) &
  566. X.fi
  567. X.PP
  568. XThe expansion of the %h (remote host) and %d (service name) sequences
  569. Xis described in the section on shell commands.
  570. X.PP
  571. XWarning: do not booby-trap your finger daemon, unless you are prepared
  572. Xfor infinite finger loops.
  573. X.PP
  574. XOn network firewall systems this trick can be carried even further.
  575. XThe typical network firewall only provides a limited set of services to
  576. Xthe outer world. All other services can be "bugged" just like the above
  577. Xtftp example. The result is an excellent early-warning system.
  578. X.br
  579. X.ne 4
  580. X.SH DIAGNOSTICS
  581. XAn error is reported when a syntax error is found in a host access
  582. Xcontrol rule; when the length of an access control rule exceeds the
  583. Xcapacity of an internal buffer; when an access control rule is not
  584. Xterminated by a newline character; when the result of %<character>
  585. Xexpansion would overflow an internal buffer; when a system call fails
  586. Xthat shouldn\'t.  All problems are reported via the syslog daemon.
  587. X.SH FILES
  588. X.na
  589. X.nf
  590. X/etc/hosts.allow, (daemon,client) pairs that are granted access.
  591. X/etc/hosts.deny, (daemon,client) pairs that are denied access.
  592. X.ad
  593. X.fi
  594. X.SH SEE ALSO
  595. Xtcpd(8) tcp/ip daemon wrapper program.
  596. X.SH BUGS
  597. XIf a name server lookup times out, the host name will not be available
  598. Xto the access control software, even though the host is registered.
  599. X.PP
  600. XDomain name server lookups are case insensitive; NIS (formerly YP)
  601. Xnetgroup lookups are case sensitive.
  602. X.SH AUTHOR
  603. X.na
  604. X.nf
  605. XWietse Venema (wietse@wzv.win.tue.nl)
  606. XDepartment of Mathematics and Computing Science
  607. XEindhoven University of Technology
  608. XDen Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  609. X\" @(#) hosts_access.5 1.11 93/03/07 22:47:47
  610. END_OF_hosts_access.5
  611. if test 9533 -ne `wc -c <hosts_access.5`; then
  612.     echo shar: \"hosts_access.5\" unpacked with wrong size!
  613. fi
  614. # end of overwriting check
  615. fi
  616. if test -f percent_x.c -a "${1}" != "-c" ; then 
  617.   echo shar: Will not over-write existing file \"percent_x.c\"
  618. else
  619. echo shar: Extracting \"percent_x.c\" \(2603 characters\)
  620. sed "s/^X//" >percent_x.c <<'END_OF_percent_x.c'
  621. X /*
  622. X  * percent_x() takes a string and performs %a (host address), %c (client
  623. X  * info), %h (host name or address), %d (daemon name), %p (process id) and
  624. X  * %u (user name) substitutions. It aborts the program when the result of
  625. X  * expansion would overflow the output buffer. Because the result of %<char>
  626. X  * expansion is typically passed on to a shell process, characters that may
  627. X  * confuse the shell are replaced by underscores.
  628. X  * 
  629. X  * Diagnostics are reported through syslog(3).
  630. X  * 
  631. X  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  632. X  */
  633. X
  634. X#ifndef lint
  635. Xstatic char sccsid[] = "@(#) percent_x.c 1.2 92/08/24 21:46:22";
  636. X#endif
  637. X
  638. X/* System libraries. */
  639. X
  640. X#include <stdio.h>
  641. X#include <syslog.h>
  642. X
  643. Xextern char *strncpy();
  644. Xextern char *strchr();
  645. Xextern void exit();
  646. X
  647. X/* Local stuff. */
  648. X
  649. X#include "log_tcp.h"
  650. X
  651. X/* percent_x - do %<char> expansion, abort if result buffer is too small */
  652. X
  653. Xvoid    percent_x(result, result_len, str, daemon, client, pid)
  654. Xchar   *result;
  655. Xint     result_len;
  656. Xchar   *str;
  657. Xchar   *daemon;
  658. Xstruct from_host *client;
  659. Xint     pid;
  660. X{
  661. X    char   *end = result + result_len - 1;    /* end of result buffer */
  662. X    char   *expansion;
  663. X    int     expansion_len;
  664. X    char    pid_buf[10];
  665. X    static char ok_chars[] = "1234567890!@%-_=+\\:,./\
  666. Xabcdefghijklmnopqrstuvwxyz\
  667. XABCDEFGHIJKLMNOPQRSTUVWXYZ";
  668. X    char   *cp;
  669. X
  670. X    /*
  671. X     * %a becomes the client address; %c all user and host information we
  672. X     * have about the client; %d the daemon process name; %h the client host
  673. X     * name or address; %p the daemon process id; %u the remote user name; %%
  674. X     * becomes a %, and %other is ignored. We terminate with a diagnostic if
  675. X     * we would overflow the result buffer. Characters that may confuse the
  676. X     * shell are mapped to underscores.
  677. X     */
  678. X
  679. X    while (*str) {
  680. X    if (*str == '%') {
  681. X        str++;
  682. X        expansion =
  683. X        *str == 'a' ? (str++, client->addr) :
  684. X        *str == 'c' ? (str++, hosts_info(client)) :
  685. X        *str == 'd' ? (str++, daemon) :
  686. X        *str == 'h' ? (str++, FROM_HOST(client)) :
  687. X        *str == 'p' ? (str++, sprintf(pid_buf, "%d", pid), pid_buf) :
  688. X        *str == 'u' ? (str++, client->user) :
  689. X        *str == '%' ? (str++, "%") :
  690. X        *str == 0 ? "" : (str++, "");
  691. X        expansion_len = strlen(expansion);
  692. X        for (cp = expansion; *cp; cp++)
  693. X        if (strchr(ok_chars, *cp) == 0)
  694. X            *cp = '_';
  695. X    } else {
  696. X        expansion = str++;
  697. X        expansion_len = 1;
  698. X    }
  699. X    if (result + expansion_len >= end) {
  700. X        syslog(LOG_ERR, "shell command too long: %30s...", result);
  701. X        exit(0);
  702. X    }
  703. X    strncpy(result, expansion, expansion_len);
  704. X    result += expansion_len;
  705. X    }
  706. X    *result = 0;
  707. X}
  708. END_OF_percent_x.c
  709. if test 2603 -ne `wc -c <percent_x.c`; then
  710.     echo shar: \"percent_x.c\" unpacked with wrong size!
  711. fi
  712. # end of overwriting check
  713. fi
  714. if test -f rfc931.c -a "${1}" != "-c" ; then 
  715.   echo shar: Will not over-write existing file \"rfc931.c\"
  716. else
  717. echo shar: Extracting \"rfc931.c\" \(3825 characters\)
  718. sed "s/^X//" >rfc931.c <<'END_OF_rfc931.c'
  719. X /*
  720. X  * rfc931_user() speaks a common subset of the RFC 931, AUTH, TAP and IDENT
  721. X  * protocols. It consults an RFC 931 etc. compatible daemon on the client
  722. X  * host to look up the remote user name. The information should not be used
  723. X  * for authentication purposes.
  724. X  * 
  725. X  * Diagnostics are reported through syslog(3).
  726. X  * 
  727. X  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  728. X  * 
  729. X  * Inspired by the authutil package (comp.sources.unix volume 22) by Dan
  730. X  * Bernstein (brnstnd@kramden.acf.nyu.edu).
  731. X  */
  732. X
  733. X#ifndef lint
  734. Xstatic char sccsid[] = "@(#) rfc931.c 1.4 93/03/07 22:47:52";
  735. X#endif
  736. X
  737. X#include <stdio.h>
  738. X#include <syslog.h>
  739. X#include <sys/types.h>
  740. X#include <sys/socket.h>
  741. X#include <netinet/in.h>
  742. X#include <setjmp.h>
  743. X#include <signal.h>
  744. X
  745. X#include "log_tcp.h"
  746. X
  747. X#define    RFC931_PORT    113        /* Semi-well-known port */
  748. X
  749. X#ifndef RFC931_TIMEOUT
  750. X#define    RFC931_TIMEOUT    30        /* wait for at most 30 seconds */
  751. X#endif
  752. X
  753. Xextern char *strchr();
  754. Xextern char *inet_ntoa();
  755. X
  756. Xstatic jmp_buf timebuf;
  757. X
  758. X/* timeout - handle timeouts */
  759. X
  760. Xstatic void timeout(sig)
  761. Xint     sig;
  762. X{
  763. X    longjmp(timebuf, sig);
  764. X}
  765. X
  766. X/* rfc931_name - return remote user name */
  767. X
  768. Xchar   *rfc931_name(there)
  769. Xstruct sockaddr_in *there;        /* remote link information */
  770. X{
  771. X    struct sockaddr_in here;        /* local link information */
  772. X    struct sockaddr_in sin;        /* for talking to RFC931 daemon */
  773. X    int     length;
  774. X    int     s;
  775. X    unsigned remote;
  776. X    unsigned local;
  777. X    static char user[256];        /* XXX */
  778. X    char    buffer[512];        /* YYY */
  779. X    FILE   *fp;
  780. X    char   *cp;
  781. X    char   *result = FROM_UNKNOWN;
  782. X
  783. X    /* Find out local address and port number of stdin. */
  784. X
  785. X    length = sizeof(here);
  786. X    if (getsockname(0, (struct sockaddr *) & here, &length) == -1) {
  787. X    syslog(LOG_ERR, "getsockname: %m");
  788. X    return (result);
  789. X    }
  790. X
  791. X    /*
  792. X     * The socket that will be used for user name lookups should be bound to
  793. X     * the same local IP address as stdin. This will automagically happen on
  794. X     * hosts that have only one IP network address. When the local host has
  795. X     * more than one IP network address, we must do an explicit bind() call.
  796. X     */
  797. X
  798. X    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  799. X    return (result);
  800. X
  801. X    sin = here;
  802. X    sin.sin_port = 0;
  803. X    if (bind(s, (struct sockaddr *) & sin, sizeof sin) < 0) {
  804. X    syslog(LOG_ERR, "bind: %s: %m", inet_ntoa(here.sin_addr));
  805. X    return (result);
  806. X    }
  807. X    /* Set up timer so we won't get stuck. */
  808. X
  809. X    signal(SIGALRM, timeout);
  810. X    if (setjmp(timebuf)) {
  811. X    close(s);                /* not: fclose(fp) */
  812. X    return (result);
  813. X    }
  814. X    alarm(RFC931_TIMEOUT);
  815. X
  816. X    /* Connect to the RFC931 daemon. */
  817. X
  818. X    sin = *there;
  819. X    sin.sin_port = htons(RFC931_PORT);
  820. X    if (connect(s, (struct sockaddr *) & sin, sizeof(sin)) == -1
  821. X    || (fp = fdopen(s, "w+")) == 0) {
  822. X    close(s);
  823. X    alarm(0);
  824. X    return (result);
  825. X    }
  826. X
  827. X    /*
  828. X     * Use unbuffered I/O or we may read back our own query. setbuf() must be
  829. X     * called before doing any I/O on the stream. Thanks for the reminder,
  830. X     * Paul Kranenburg <pk@cs.few.eur.nl>!
  831. X     */
  832. X
  833. X    setbuf(fp, (char *) 0);
  834. X
  835. X    /* Query the RFC 931 server. Would 13-byte writes ever be broken up? */
  836. X
  837. X    fprintf(fp, "%u,%u\r\n", ntohs(there->sin_port), ntohs(here.sin_port));
  838. X    fflush(fp);
  839. X
  840. X    /*
  841. X     * Read response from server. Use fgets()/sscanf() instead of fscanf()
  842. X     * because there is no buffer for pushback. Thanks, Chris Turbeville
  843. X     * <turbo@cse.uta.edu>.
  844. X     */
  845. X
  846. X    if (fgets(buffer, sizeof(buffer), fp) != 0
  847. X    && ferror(fp) == 0 && feof(fp) == 0
  848. X    && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s",
  849. X          &remote, &local, user) == 3
  850. X    && ntohs(there->sin_port) == remote
  851. X    && ntohs(here.sin_port) == local) {
  852. X    /* Strip trailing carriage return. */
  853. X
  854. X    if (cp = strchr(user, '\r'))
  855. X        *cp = 0;
  856. X    result = user;
  857. X    }
  858. X    alarm(0);
  859. X    fclose(fp);
  860. X    return (result);
  861. X}
  862. END_OF_rfc931.c
  863. if test 3825 -ne `wc -c <rfc931.c`; then
  864.     echo shar: \"rfc931.c\" unpacked with wrong size!
  865. fi
  866. # end of overwriting check
  867. fi
  868. if test -f shell_cmd.c -a "${1}" != "-c" ; then 
  869.   echo shar: Will not over-write existing file \"shell_cmd.c\"
  870. else
  871. echo shar: Extracting \"shell_cmd.c\" \(2883 characters\)
  872. sed "s/^X//" >shell_cmd.c <<'END_OF_shell_cmd.c'
  873. X /*
  874. X  * shell_cmd() takes a shell command template and performs %a (host
  875. X  * address), %c (client info), %h (host name or address), %d (daemon name),
  876. X  * %p (process id) and %u (user name) substitutions. The result is executed
  877. X  * by a /bin/sh child process, with standard input, standard output and
  878. X  * standard error connected to /dev/null.
  879. X  * 
  880. X  * Diagnostics are reported through syslog(3).
  881. X  * 
  882. X  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  883. X  */
  884. X
  885. X#ifndef lint
  886. Xstatic char sccsid[] = "@(#) shell_cmd.c 1.3 93/03/07 22:47:39";
  887. X#endif
  888. X
  889. X/* System libraries. */
  890. X
  891. X#include <sys/types.h>
  892. X#include <sys/param.h>
  893. X#include <signal.h>
  894. X#include <stdio.h>
  895. X#include <syslog.h>
  896. X
  897. Xextern char *strncpy();
  898. Xextern void closelog();
  899. Xextern void exit();
  900. X
  901. X/* Local stuff. */
  902. X
  903. X#include "log_tcp.h"
  904. X
  905. X/* Forward declarations. */
  906. X
  907. Xstatic void do_child();
  908. X
  909. X/* shell_cmd - expand %<char> sequences and execute shell command */
  910. X
  911. Xvoid    shell_cmd(string, daemon, client)
  912. Xchar   *string;
  913. Xchar   *daemon;
  914. Xstruct from_host *client;
  915. X{
  916. X    char    cmd[BUFSIZ];
  917. X    int     child_pid;
  918. X    int     wait_pid;
  919. X    int     daemon_pid = getpid();
  920. X
  921. X    /*
  922. X     * Most of the work is done within the child process, to minimize the
  923. X     * risk of damage to the parent.
  924. X     */
  925. X
  926. X    switch (child_pid = fork()) {
  927. X    case -1:                    /* error */
  928. X    syslog(LOG_ERR, "fork: %m");
  929. X    break;
  930. X    case 00:                    /* child */
  931. X    percent_x(cmd, sizeof(cmd), string, daemon, client, daemon_pid);
  932. X    do_child(daemon, cmd);
  933. X    /* NOTREACHED */
  934. X    default:                    /* parent */
  935. X    while ((wait_pid = wait((int *) 0)) != -1 && wait_pid != child_pid)
  936. X         /* void */ ;
  937. X    }
  938. X}
  939. X
  940. X/* do_child - exec command with { stdin, stdout, stderr } to /dev/null */
  941. X
  942. Xstatic void do_child(myname, command)
  943. Xchar   *myname;
  944. Xchar   *command;
  945. X{
  946. X    char   *error = 0;
  947. X    int     tmp_fd;
  948. X
  949. X    /*
  950. X     * SunOS 4.x may send a SIGHUP to grandchildren if the child exits first.
  951. X     * Sessions and process groups make old and grown-up programmers tear out
  952. X     * what little hair is left and run away crying.
  953. X     */
  954. X
  955. X    signal(SIGHUP, SIG_IGN);
  956. X
  957. X    /*
  958. X     * Close a bunch of file descriptors. The Ultrix inetd only passes stdin,
  959. X     * but other inetd implementations set up stdout as well. Ignore errors.
  960. X     */
  961. X
  962. X    closelog();
  963. X    for (tmp_fd = 0; tmp_fd < 10; tmp_fd++)
  964. X    (void) close(tmp_fd);
  965. X
  966. X    /* Set up new stdin, stdout, stderr, and exec the shell command. */
  967. X
  968. X    if (open("/dev/null", 2) != 0) {
  969. X    error = "open /dev/null: %m";
  970. X    } else if (dup(0) != 1 || dup(0) != 2) {
  971. X    error = "dup: %m";
  972. X    } else {
  973. X    (void) execl("/bin/sh", "sh", "-c", command, (char *) 0);
  974. X    error = "execl /bin/sh: %m";
  975. X    }
  976. X
  977. X    /* We can reach the following code only if there was an error. */
  978. X
  979. X#ifdef LOG_MAIL
  980. X    (void) openlog(myname, LOG_PID, FACILITY);
  981. X#else
  982. X    (void) openlog(myname, LOG_PID);
  983. X#endif
  984. X    syslog(LOG_ERR, error);
  985. X    exit(0);
  986. X}
  987. END_OF_shell_cmd.c
  988. if test 2883 -ne `wc -c <shell_cmd.c`; then
  989.     echo shar: \"shell_cmd.c\" unpacked with wrong size!
  990. fi
  991. # end of overwriting check
  992. fi
  993. if test -f strcasecmp.c -a "${1}" != "-c" ; then 
  994.   echo shar: Will not over-write existing file \"strcasecmp.c\"
  995. else
  996. echo shar: Extracting \"strcasecmp.c\" \(3767 characters\)
  997. sed "s/^X//" >strcasecmp.c <<'END_OF_strcasecmp.c'
  998. X/*
  999. X * Copyright (c) 1987 Regents of the University of California.
  1000. X * All rights reserved.
  1001. X *
  1002. X * Redistribution and use in source and binary forms are permitted
  1003. X * provided that the above copyright notice and this paragraph are
  1004. X * duplicated in all such forms and that any documentation,
  1005. X * advertising materials, and other materials related to such
  1006. X * distribution and use acknowledge that the software was developed
  1007. X * by the University of California, Berkeley.  The name of the
  1008. X * University may not be used to endorse or promote products derived
  1009. X * from this software without specific prior written permission.
  1010. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  1011. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  1012. X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  1013. X */
  1014. X
  1015. X#if defined(LIBC_SCCS) && !defined(lint)
  1016. Xstatic char sccsid[] = "@(#)strcasecmp.c    5.6 (Berkeley) 6/27/88";
  1017. X#endif /* LIBC_SCCS and not lint */
  1018. X
  1019. X#include <sys/types.h>
  1020. X
  1021. X/*
  1022. X * This array is designed for mapping upper and lower case letter
  1023. X * together for a case independent comparison.  The mappings are
  1024. X * based upon ascii character sequences.
  1025. X */
  1026. Xstatic u_char charmap[] = {
  1027. X    '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
  1028. X    '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
  1029. X    '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
  1030. X    '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
  1031. X    '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
  1032. X    '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
  1033. X    '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
  1034. X    '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
  1035. X    '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
  1036. X    '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
  1037. X    '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
  1038. X    '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
  1039. X    '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
  1040. X    '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
  1041. X    '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
  1042. X    '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
  1043. X    '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
  1044. X    '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
  1045. X    '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
  1046. X    '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
  1047. X    '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
  1048. X    '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
  1049. X    '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
  1050. X    '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
  1051. X    '\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
  1052. X    '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
  1053. X    '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
  1054. X    '\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
  1055. X    '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
  1056. X    '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
  1057. X    '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
  1058. X    '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
  1059. X};
  1060. X
  1061. Xstrcasecmp(s1, s2)
  1062. X    char *s1, *s2;
  1063. X{
  1064. X    register u_char    *cm = charmap,
  1065. X            *us1 = (u_char *)s1,
  1066. X            *us2 = (u_char *)s2;
  1067. X
  1068. X    while (cm[*us1] == cm[*us2++])
  1069. X        if (*us1++ == '\0')
  1070. X            return(0);
  1071. X    return(cm[*us1] - cm[*--us2]);
  1072. X}
  1073. X
  1074. Xstrncasecmp(s1, s2, n)
  1075. X    char *s1, *s2;
  1076. X    register int n;
  1077. X{
  1078. X    register u_char    *cm = charmap,
  1079. X            *us1 = (u_char *)s1,
  1080. X            *us2 = (u_char *)s2;
  1081. X
  1082. X    while (--n >= 0 && cm[*us1] == cm[*us2++])
  1083. X        if (*us1++ == '\0')
  1084. X            return(0);
  1085. X    return(n < 0 ? 0 : cm[*us1] - cm[*--us2]);
  1086. X}
  1087. END_OF_strcasecmp.c
  1088. if test 3767 -ne `wc -c <strcasecmp.c`; then
  1089.     echo shar: \"strcasecmp.c\" unpacked with wrong size!
  1090. fi
  1091. # end of overwriting check
  1092. fi
  1093. if test -f strtok.c -a "${1}" != "-c" ; then 
  1094.   echo shar: Will not over-write existing file \"strtok.c\"
  1095. else
  1096. echo shar: Extracting \"strtok.c\" \(2976 characters\)
  1097. sed "s/^X//" >strtok.c <<'END_OF_strtok.c'
  1098. X/*
  1099. X * Copyright (c) 1988 Regents of the University of California.
  1100. X * All rights reserved.
  1101. X *
  1102. X * Redistribution and use in source and binary forms, with or without
  1103. X * modification, are permitted provided that the following conditions
  1104. X * are met:
  1105. X * 1. Redistributions of source code must retain the above copyright
  1106. X *    notice, this list of conditions and the following disclaimer.
  1107. X * 2. Redistributions in binary form must reproduce the above copyright
  1108. X *    notice, this list of conditions and the following disclaimer in the
  1109. X *    documentation and/or other materials provided with the distribution.
  1110. X * 3. All advertising materials mentioning features or use of this software
  1111. X *    must display the following acknowledgement:
  1112. X *    This product includes software developed by the University of
  1113. X *    California, Berkeley and its contributors.
  1114. X * 4. Neither the name of the University nor the names of its contributors
  1115. X *    may be used to endorse or promote products derived from this software
  1116. X *    without specific prior written permission.
  1117. X *
  1118. X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  1119. X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  1120. X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  1121. X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  1122. X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  1123. X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  1124. X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  1125. X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  1126. X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  1127. X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  1128. X * SUCH DAMAGE.
  1129. X */
  1130. X
  1131. X#if defined(LIBC_SCCS) && !defined(lint)
  1132. Xstatic char sccsid[] = "@(#)strtok.c    5.8 (Berkeley) 2/24/91";
  1133. X#endif /* LIBC_SCCS and not lint */
  1134. X
  1135. X#ifdef __STDC__    /* Added for backwards compatibility -- WZV 930122 */
  1136. X#include <stddef.h>
  1137. X#include <string.h>
  1138. X#else
  1139. X#define const
  1140. X#define NULL 0
  1141. X#endif
  1142. X
  1143. Xchar *
  1144. Xstrtok(s, delim)
  1145. X    register char *s;
  1146. X    register const char *delim;
  1147. X{
  1148. X    register char *spanp;
  1149. X    register int c, sc;
  1150. X    char *tok;
  1151. X    static char *last;
  1152. X
  1153. X
  1154. X    if (s == NULL && (s = last) == NULL)
  1155. X        return (NULL);
  1156. X
  1157. X    /*
  1158. X     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
  1159. X     */
  1160. Xcont:
  1161. X    c = *s++;
  1162. X    for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
  1163. X        if (c == sc)
  1164. X            goto cont;
  1165. X    }
  1166. X
  1167. X    if (c == 0) {        /* no non-delimiter characters */
  1168. X        last = NULL;
  1169. X        return (NULL);
  1170. X    }
  1171. X    tok = s - 1;
  1172. X
  1173. X    /*
  1174. X     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
  1175. X     * Note that delim must have one NUL; we stop if we see that, too.
  1176. X     */
  1177. X    for (;;) {
  1178. X        c = *s++;
  1179. X        spanp = (char *)delim;
  1180. X        do {
  1181. X            if ((sc = *spanp++) == c) {
  1182. X                if (c == 0)
  1183. X                    s = NULL;
  1184. X                else
  1185. X                    s[-1] = 0;
  1186. X                last = s;
  1187. X                return (tok);
  1188. X            }
  1189. X        } while (sc != 0);
  1190. X    }
  1191. X    /* NOTREACHED */
  1192. X}
  1193. END_OF_strtok.c
  1194. if test 2976 -ne `wc -c <strtok.c`; then
  1195.     echo shar: \"strtok.c\" unpacked with wrong size!
  1196. fi
  1197. # end of overwriting check
  1198. fi
  1199. if test -f tcpd.8 -a "${1}" != "-c" ; then 
  1200.   echo shar: Will not over-write existing file \"tcpd.8\"
  1201. else
  1202. echo shar: Extracting \"tcpd.8\" \(6757 characters\)
  1203. sed "s/^X//" >tcpd.8 <<'END_OF_tcpd.8'
  1204. X.TH TCPD 8
  1205. X.SH NAME
  1206. Xtcpd \- access control facility for internet services
  1207. X.SH DESCRIPTION
  1208. X.PP
  1209. XThe \fItcpd\fR program can be set up to monitor incoming requests for
  1210. X\fItelnet\fR, \fIfinger\fR, \fIftp\fR, \fIexec\fR, \fIrsh\fR,
  1211. X\fIrlogin\fR, \fItftp\fR, \fItalk\fR, \fIcomsat\fR and other services
  1212. Xthat have a one-to-one mapping onto executable files.
  1213. X.PP
  1214. XOperation is as follows: whenever a request for internet service
  1215. Xarrives, the \fIinetd\fP daemon is tricked into running the \fItcpd\fP
  1216. Xprogram instead of the desired server. \fItcpd\fP logs the request and
  1217. Xdoes some additional checks. When all is well, \fItcpd\fP runs the
  1218. Xappropriate server program and goes away.
  1219. X.PP
  1220. XOptional features are: pattern-based access control, patterns, remote
  1221. Xusername lookups with the RFC 931 protocol, protection against hosts
  1222. Xthat pretend to have someone elses host name, and protection against
  1223. Xhosts that pretend to have someone elses network address.
  1224. X.SH LOGGING
  1225. XConnections that are monitored by
  1226. X.I tcpd
  1227. Xare reported through the \fIsyslog\fR(3) facility. Each record contains
  1228. Xa time stamp, the remote host name and the name of the service
  1229. Xrequested. The information can be useful to detect unwanted activities,
  1230. Xespecially when logfile information from several hosts is merged.
  1231. X.PP
  1232. XIn order to find out where your logs are going, examine the syslog
  1233. Xconfiguration file, usually /etc/syslog.conf.
  1234. X.SH ACCESS CONTROL
  1235. XOptionally,
  1236. X.I tcpd
  1237. Xsupports a simple form of access control that is based on pattern
  1238. Xmatching.  The access-control software provides hooks for the execution
  1239. Xof shell commands when a pattern fires.  For details, see the
  1240. X\fIhosts_access\fR(5) manual page.
  1241. X.SH HOST NAME VERIFICATION
  1242. XThe authentication scheme of some protocols (\fIrlogin, rsh\fR) relies
  1243. Xon host names. Some implementations believe the host name that they get
  1244. Xfrom any random name server; other implementations are more careful but
  1245. Xuse a flawed algorithm.
  1246. X.PP
  1247. X.I tcpd
  1248. Xverifies the remote host name that is returned by the address->name DNS
  1249. Xserver by looking at the host name and address that are returned by the
  1250. Xname->address DNS server.  If any discrepancy is detected,
  1251. X.I tcpd
  1252. Xconcludes that it is dealing with a host that pretends to have someone
  1253. Xelses host name.
  1254. X.PP
  1255. XIf the sources are compiled with the \*QPARANOID\*U option,
  1256. X.I tcpd
  1257. Xwill drop the connection in case of a host name/address mismatch.
  1258. XOtherwise,
  1259. X.I tcpd
  1260. Xjust pretends that host name lookup failed when logging the connection
  1261. Xand consulting the optional access control tables.
  1262. X.SH HOST ADDRESS SPOOFING
  1263. XBy default,
  1264. X.I tcpd
  1265. Xdisables source-routing socket options on every connection that it
  1266. Xdeals with. This will take care of most attacks from hosts that pretend
  1267. Xto have an address that belongs to someone elses network. UDP services
  1268. Xdo not benefit from this protection.
  1269. X.SH RFC 931
  1270. XWhen RFC 931 lookups are enabled (compile-time option) \fItcpd\fR will
  1271. Xattempt to establish the name of the remote user. This will succeed
  1272. Xonly if the client host runs an RFC 931-compliant daemon.  Remote user
  1273. Xname lookups will not work for datagram-oriented connections, and may
  1274. Xcause noticeable delays in the case of connections from PCs.
  1275. X.SH EXAMPLES
  1276. XThe details of using \fItcpd\fR depend on pathname information that was
  1277. Xcompiled into the program.
  1278. X.SH EXAMPLE 1
  1279. XThis example applies when \fItcpd\fR expects that the original network
  1280. Xdaemons will be moved to a "secret" place.
  1281. X.PP
  1282. XIn order to monitor access to the \fIfinger\fR service, move the
  1283. Xoriginal finger daemon to the "secret" place and install tcpd in the
  1284. Xplace of the original finger daemon. No changes are required to
  1285. Xconfiguration files.
  1286. X.nf
  1287. X.sp
  1288. X.in +5
  1289. X# mkdir /secret/place
  1290. X# mv /usr/etc/in.fingerd /secret/place
  1291. X# cp tcpd /usr/etc/in.fingerd
  1292. X.fi
  1293. X.PP
  1294. XThe example assumes that the network daemons live in /usr/etc. On some
  1295. Xsystems, network daemons live in /usr/sbin or in /usr/libexec, or have
  1296. Xno `in.\' prefix to their name.
  1297. X.SH EXAMPLE 2
  1298. XThis example applies when \fItcpd\fR expects that the network daemons
  1299. Xare left in their original place.
  1300. X.PP
  1301. XIn order to monitor access to the \fIfinger\fR service, perform the
  1302. Xfollowing edits on the \fIinetd\fR configuration file (usually 
  1303. X\fI/etc/inetd.conf\fR or \fI/etc/inet/inetd.conf\fR):
  1304. X.nf
  1305. X.sp
  1306. X.ti +5
  1307. Xfinger  stream  tcp  nowait  nobody  /usr/etc/in.fingerd  in.fingerd
  1308. X.sp
  1309. Xbecomes:
  1310. X.sp
  1311. X.ti +5
  1312. Xfinger  stream  tcp  nowait  nobody  /some/where/tcpd     in.fingerd
  1313. X.sp
  1314. X.fi
  1315. X.PP
  1316. XThe example assumes that the network daemons live in /usr/etc. On some
  1317. Xsystems, network daemons live in /usr/sbin or in /usr/libexec, the
  1318. Xdaemons have no `in.\' prefix to their name, or there is no userid
  1319. Xfield in the inetd configuration file.
  1320. X.PP
  1321. XSimilar changes will be needed for the other services that are to be
  1322. Xcovered by \fItcpd\fR.  Send a `kill -HUP\' to the \fIinetd\fR(8)
  1323. Xprocess to make the changes effective.
  1324. X.SH EXAMPLE 3
  1325. XIn the case of daemons that do not live in a common directory ("secret"
  1326. Xor otherwise), edit the \fIinetd\fR configuration file so that it
  1327. Xspecifies an absolute path name for the process name field. For example:
  1328. X.nf
  1329. X.sp
  1330. X    ntalk  dgram  udp  wait  root  /some/where/tcpd  /usr/local/lib/ntalkd
  1331. X.sp
  1332. X.fi
  1333. X.PP
  1334. XOnly the last component (ntalkd) of the process name will be used for
  1335. Xaccess control and logging.
  1336. X.SH BUGS
  1337. XSome UDP (and RPC) daemons linger around for a while after they have
  1338. Xfinished their work, in case another request comes in.  In the inetd
  1339. Xconfiguration file these services are registered with the \fIwait\fR
  1340. Xoption. Only the request that started such a daemon will be logged.
  1341. X.PP
  1342. XThe program does not work with RPC services over TCP. These services
  1343. Xare registered as \fIrpc/tcp\fR in the inetd configuration file. The
  1344. Xonly non-trivial service that is affected by this limitation is
  1345. X\fIrexd\fR, which is used by the \fIon(1)\fR command. This is no great
  1346. Xloss.  On most systems, \fIrexd\fR is less secure than a wildcard in
  1347. X/etc/hosts.equiv.
  1348. X.PP
  1349. XRPC broadcast requests (for example: \fIrwall, rup, rusers\fR) always
  1350. Xappear to come from the responding host. What happens is that the
  1351. Xclient broadcasts the request to all \fIportmap\fR daemons on its
  1352. Xnetwork; each \fIportmap\fR daemon forwards the request to a local
  1353. Xdaemon. As far as the \fIrwall\fR etc.  daemons know, the request comes
  1354. Xfrom the local host.
  1355. X.SH FILES
  1356. X.PP
  1357. XThe default locations of the host access control tables are:
  1358. X.PP
  1359. X/etc/hosts.allow
  1360. X.br
  1361. X/etc/hosts.deny
  1362. X.SH SEE ALSO
  1363. X.na
  1364. X.nf
  1365. Xhosts_access(5), format of the tcpd access control tables.
  1366. Xsyslog.conf(5), format of the syslogd control file.
  1367. Xinetd.conf(5), format of the inetd control file.
  1368. X.SH AUTHORS
  1369. X.na
  1370. X.nf
  1371. XWietse Venema (wietse@wzv.win.tue.nl),
  1372. XDepartment of Mathematics and Computing Science,
  1373. XEindhoven University of Technology,
  1374. XThe Netherlands.
  1375. X\" @(#) tcpd.8 1.2 93/03/07 22:47:54
  1376. END_OF_tcpd.8
  1377. if test 6757 -ne `wc -c <tcpd.8`; then
  1378.     echo shar: \"tcpd.8\" unpacked with wrong size!
  1379. fi
  1380. # end of overwriting check
  1381. fi
  1382. if test -f tcpd.c -a "${1}" != "-c" ; then 
  1383.   echo shar: Will not over-write existing file \"tcpd.c\"
  1384. else
  1385. echo shar: Extracting \"tcpd.c\" \(2995 characters\)
  1386. sed "s/^X//" >tcpd.c <<'END_OF_tcpd.c'
  1387. X /*
  1388. X  * General front end for stream and datagram IP services. This program logs
  1389. X  * the remote host name and then invokes the real daemon. For example,
  1390. X  * install as /usr/etc/{tftpd,fingerd,telnetd,ftpd,rlogind,rshd,rexecd},
  1391. X  * after saving the real daemons in the directory "/usr/etc/...". This
  1392. X  * arrangement requires that the network daemons are started by inetd or
  1393. X  * something similar. Connections and diagnostics are logged through
  1394. X  * syslog(3).
  1395. X  * 
  1396. X  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  1397. X  */
  1398. X
  1399. X#ifndef lint
  1400. Xstatic char sccsid[] = "@(#) tcpd.c 1.4 93/03/07 22:47:32";
  1401. X#endif
  1402. X
  1403. X/* System libraries. */
  1404. X
  1405. X#include <sys/types.h>
  1406. X#include <sys/param.h>
  1407. X#include <sys/stat.h>
  1408. X#include <stdio.h>
  1409. X#include <syslog.h>
  1410. X
  1411. Xextern char *strrchr();
  1412. Xextern char *strcpy();
  1413. X
  1414. X#ifndef MAXPATHNAMELEN
  1415. X#define MAXPATHNAMELEN    BUFSIZ
  1416. X#endif
  1417. X
  1418. X/* Local stuff. */
  1419. X
  1420. X#include "patchlevel.h"
  1421. X#include "log_tcp.h"
  1422. X
  1423. X/* The following specifies where the vendor-provided daemons should go. */
  1424. X
  1425. X#ifndef REAL_DAEMON_DIR
  1426. X#define REAL_DAEMON_DIR    "/usr/etc/..."
  1427. X#endif
  1428. X
  1429. Xint     log_severity = SEVERITY;    /* run-time adjustable */
  1430. X
  1431. Xmain(argc, argv)
  1432. Xint     argc;
  1433. Xchar  **argv;
  1434. X{
  1435. X    struct from_host from;
  1436. X    int     from_stat;
  1437. X    char    path[MAXPATHNAMELEN];
  1438. X
  1439. X    /* Attempt to prevent the creation of world-writable files. */
  1440. X
  1441. X#ifdef DAEMON_UMASK
  1442. X    umask(DAEMON_UMASK);
  1443. X#endif
  1444. X
  1445. X    /*
  1446. X     * If argv[0] is an absolute path name, ignore REAL_DAEMON_DIR, and strip
  1447. X     * argv[0] to its basename.
  1448. X     */
  1449. X
  1450. X    if (argv[0][0] == '/') {
  1451. X    strcpy(path, argv[0]);
  1452. X    argv[0] = strrchr(argv[0], '/') + 1;
  1453. X    } else {
  1454. X    sprintf(path, "%s/%s", REAL_DAEMON_DIR, argv[0]);
  1455. X    }
  1456. X
  1457. X    /*
  1458. X     * Open a channel to the syslog daemon. Older versions of openlog()
  1459. X     * require only two arguments.
  1460. X     */
  1461. X
  1462. X#ifdef LOG_MAIL
  1463. X    (void) openlog(argv[0], LOG_PID, FACILITY);
  1464. X#else
  1465. X    (void) openlog(argv[0], LOG_PID);
  1466. X#endif
  1467. X
  1468. X    /*
  1469. X     * Find out and verify the remote host name. Sites concerned with
  1470. X     * security may choose to refuse connections from hosts that pretend to
  1471. X     * have someone elses host name.
  1472. X     */
  1473. X
  1474. X    from_stat = fromhost(&from);
  1475. X#ifdef PARANOID
  1476. X    if (from_stat == -1)
  1477. X    refuse(&from);
  1478. X#endif
  1479. X
  1480. X    /*
  1481. X     * The BSD rlogin and rsh daemons that came out after 4.3 BSD disallow
  1482. X     * socket options at the IP level. They do so for a good reason. Let's
  1483. X     * follow their example.
  1484. X     */
  1485. X
  1486. X#ifdef KILL_IP_OPTIONS
  1487. X    fix_options(&from);
  1488. X#endif
  1489. X
  1490. X    /*
  1491. X     * Check whether this host can access the service in argv[0]. The
  1492. X     * access-control code invokes optional shell commands as specified in
  1493. X     * the access-control tables.
  1494. X     */
  1495. X
  1496. X#ifdef HOSTS_ACCESS
  1497. X    if (!hosts_access(argv[0], &from))
  1498. X    refuse(&from);
  1499. X#endif
  1500. X
  1501. X    /* Report remote client and invoke the real daemon program. */
  1502. X
  1503. X    syslog(log_severity, "connect from %s", hosts_info(&from));
  1504. X    (void) execv(path, argv);
  1505. X    syslog(LOG_ERR, "%s: %m", path);
  1506. X    clean_exit(&from);
  1507. X    /* NOTREACHED */
  1508. X}
  1509. END_OF_tcpd.c
  1510. if test 2995 -ne `wc -c <tcpd.c`; then
  1511.     echo shar: \"tcpd.c\" unpacked with wrong size!
  1512. fi
  1513. # end of overwriting check
  1514. fi
  1515. if test -f try.c -a "${1}" != "-c" ; then 
  1516.   echo shar: Will not over-write existing file \"try.c\"
  1517. else
  1518. echo shar: Extracting \"try.c\" \(3200 characters\)
  1519. sed "s/^X//" >try.c <<'END_OF_try.c'
  1520. X /*
  1521. X  * try - program to try out host access-control tables, including the
  1522. X  * optional shell commands.
  1523. X  * 
  1524. X  * usage: try process_name host_name_or_address
  1525. X  * 
  1526. X  * where process_name is a daemon process name (argv[0] value). If a host name
  1527. X  * is specified, both the name and address will be used to check the address
  1528. X  * control tables. If a host address is specified, the program pretends that
  1529. X  * host name lookup failed.
  1530. X  * 
  1531. X  * Most errors will be reported to the syslog daemon, so you'd better keep a
  1532. X  * tail on the logfile.
  1533. X  */
  1534. X
  1535. X#ifndef lint
  1536. Xstatic char sccsid[] = "@(#) try.c 1.3 93/03/07 22:47:43";
  1537. X#endif
  1538. X
  1539. X#include <sys/types.h>
  1540. X#include <netinet/in.h>
  1541. X#include <arpa/inet.h>
  1542. X#include <netdb.h>
  1543. X#include <stdio.h>
  1544. X#include <syslog.h>
  1545. X
  1546. X#ifdef HOSTS_ACCESS
  1547. X
  1548. X#ifndef    INADDR_NONE
  1549. X#define    INADDR_NONE    (-1)        /* XXX should be 0xffffffff */
  1550. X#endif
  1551. X
  1552. X#include "log_tcp.h"
  1553. X
  1554. X#ifdef INET_ADDR_BUG
  1555. X#include "inet_addr_fix"
  1556. X#endif
  1557. X
  1558. Xint     log_severity = SEVERITY;    /* run-time adjustable */
  1559. X
  1560. X/* Try out a (daemon,client) pair */
  1561. X
  1562. Xtry(daemon, name, addr)
  1563. Xchar   *daemon;
  1564. Xchar   *name;
  1565. Xchar   *addr;
  1566. X{
  1567. X    printf(" Daemon:   %s\n", daemon);
  1568. X    printf(" Hostname: %s\n", name);
  1569. X    printf(" Address:  %s\n", addr);
  1570. X    printf(" Access:   %s\n",
  1571. X       hosts_ctl(daemon, name, addr, "you") ? "granted" : "denied");
  1572. X}
  1573. X
  1574. X/* function to intercept the real shell_cmd() */
  1575. X
  1576. Xvoid    shell_cmd(cmd, daemon, client)
  1577. Xchar   *cmd;
  1578. Xchar   *daemon;
  1579. Xstruct from_host *client;
  1580. X{
  1581. X    char    buf[BUFSIZ];
  1582. X    int     pid = getpid();
  1583. X
  1584. X    percent_x(buf, sizeof(buf), cmd, daemon, client, pid);
  1585. X    printf(" Command:  %s\n", buf);
  1586. X}
  1587. X
  1588. X/* function to intercept the real process_options() */
  1589. X
  1590. Xprocess_options(options, daemon, client)
  1591. Xchar   *options;
  1592. Xchar   *daemon;
  1593. Xstruct from_host *client;
  1594. X{
  1595. X    char    buf[BUFSIZ];
  1596. X    int     pid = getpid();
  1597. X
  1598. X    percent_x(buf, sizeof(buf), options, daemon, client, pid);
  1599. X    printf(" Options:  %s\n", buf);
  1600. X}
  1601. X
  1602. Xmain(argc, argv)
  1603. Xint     argc;
  1604. Xchar  **argv;
  1605. X{
  1606. X    struct hostent *hp;
  1607. X
  1608. X#ifdef LOG_MAIL
  1609. X    openlog(argv[0], LOG_PID, FACILITY);
  1610. X#else
  1611. X    openlog(argv[0], LOG_PID);
  1612. X#endif
  1613. X
  1614. X    /*
  1615. X     * Abuse inet_addr() to find out if a host name or address was specified.
  1616. X     * 
  1617. X     * If a host address is specified, pretend that the host name lookup failed.
  1618. X     * This allows us to simulate the effect of host name lookup failures.
  1619. X     * 
  1620. X     * If a host name is specified, insist that the address is known. The reason
  1621. X     * for giving up is that in real life, a host address is always
  1622. X     * available.
  1623. X     */
  1624. X
  1625. X    if (argc != 3) {
  1626. X    fprintf(stderr, "usage: %s process_name host_name_or_address\n",
  1627. X        argv[0]);
  1628. X    return (1);
  1629. X    }
  1630. X    if (inet_addr(argv[2]) != INADDR_NONE) {    /* address specified */
  1631. X    try(argv[1], FROM_UNKNOWN, argv[2]);
  1632. X    return (0);
  1633. X    }
  1634. X    if ((hp = gethostbyname(argv[2])) == 0) {    /* address lookup fails */
  1635. X    fprintf(stderr, "host %s: address lookup failed\n", argv[2]);
  1636. X    return (1);
  1637. X    }
  1638. X    while (hp->h_addr_list[0])            /* name and address known */
  1639. X    try(argv[1], hp->h_name,
  1640. X        inet_ntoa(*(struct in_addr *) * hp->h_addr_list++));
  1641. X    return (0);
  1642. X}
  1643. X
  1644. X#else
  1645. X
  1646. Xmain()
  1647. X{
  1648. X    fprintf(stderr, "host access control is not enabled.\n");
  1649. X    return (1);
  1650. X}
  1651. X
  1652. X#endif
  1653. END_OF_try.c
  1654. if test 3200 -ne `wc -c <try.c`; then
  1655.     echo shar: \"try.c\" unpacked with wrong size!
  1656. fi
  1657. # end of overwriting check
  1658. fi
  1659. echo shar: End of archive 2 \(of 3\).
  1660. cp /dev/null ark2isdone
  1661. MISSING=""
  1662. for I in 1 2 3 ; do
  1663.     if test ! -f ark${I}isdone ; then
  1664.     MISSING="${MISSING} ${I}"
  1665.     fi
  1666. done
  1667. if test "${MISSING}" = "" ; then
  1668.     echo You have unpacked all 3 archives.
  1669.     rm -f ark[1-9]isdone
  1670. else
  1671.     echo You still need to unpack the following archives:
  1672.     echo "        " ${MISSING}
  1673. fi
  1674. ##  End of shell archive.
  1675. exit 0
  1676.  
  1677. exit 0 # Just in case...
  1678.